package database

import (
	"fmt"
	"gameServer/log" // 日志包，用于记录数据库管理器的操作日志
	"sync"           // 提供同步原语，确保管理器在并发环境下的安全操作
)

/*
本文件实现了数据库管理器（Manager），用于统一管理多个不同类型的数据库实例（如MySQL、Redis等），
提供添加、移除、批量关闭、连接检查等功能，简化多数据库场景下的运维和资源控制。
*/

// Manager 数据库管理器，负责集中管理多个数据库实例，
// 支持并发安全的添加、移除、查询等操作，适配多数据库共存的场景
type Manager struct {
	database map[string]Database // 存储数据库实例的映射表：key为数据库的唯一标识名称（如"user_mysql"、"cache_redis"），value为实现Database接口的数据库实例
	mu       sync.RWMutex        // 读写锁，保证并发安全：读操作（如获取、检查）用RLock，写操作（如添加、移除）用Lock
}

// NewManager 创建一个数据库管理器实例，初始化内部存储结构
// 返回值：
//
//	*Manager: 初始化后的数据库管理器实例
func NewManager() *Manager {
	return &Manager{
		database: make(map[string]Database), // 初始化数据库映射表为空map
	}
}

// AddDatabase 向管理器中添加一个数据库实例，并自动建立连接
// 参数：
//
//	name: 数据库的唯一标识名称（用于后续查询、移除等操作，需保证唯一）
//	db: 实现Database接口的数据库实例（如*MySqlDatabase、*RedisDatabase）
//
// 返回值：
//
//	error: 添加成功返回nil；连接数据库失败或其他错误返回具体错误信息
func (m *Manager) AddDatabase(name string, db Database) error {
	m.mu.Lock()         // 加写锁，防止并发添加导致的map竞争
	defer m.mu.Unlock() // 函数退出时自动释放锁

	// 先尝试连接数据库，连接成功后再添加到管理器
	if err := db.Connect(); err != nil {
		return err // 连接失败，直接返回错误，不添加到管理器
	}

	m.database[name] = db // 连接成功，将数据库实例存入映射表，key为name
	return nil            // 添加成功
}

// GetDatabase 通过数据库名 获取数据库
func (m *Manager) GetDatabase(name string) (Database, error) {
	m.mu.RLock()
	defer m.mu.RUnlock()

	db, exists := m.database[name]
	if !exists {
		return nil, fmt.Errorf("database %s not found", name)
	}

	return db, nil
}

// RemoveDatabase 从管理器中移除指定名称的数据库实例，并断开其连接
// 参数：
//
//	name: 要移除的数据库的唯一标识名称
//
// 返回值：
//
//	error: 移除成功返回nil；断开连接失败返回具体错误信息（即使断开失败，仍会从管理器中移除实例）
func (m *Manager) RemoveDatabase(name string) error {
	m.mu.Lock()         // 加写锁，防止并发移除导致的map竞争
	defer m.mu.Unlock() // 函数退出时自动释放锁

	// 检查数据库实例是否存在
	if db, exists := m.database[name]; exists {
		// 尝试断开数据库连接
		if err := db.Disconnect(); err != nil {
			return err // 断开连接失败，返回错误（但仍会继续移除）
		}
		delete(m.database, name) // 从映射表中删除该数据库实例
	}

	return nil // 无论是否存在该实例，均返回nil（不存在时视为已移除）
}

// GetAllDatabase 获取管理器中所有的数据库实例
// 返回值：
//
//	map[string]Database: 包含所有数据库实例的映射表副本（key为名称，value为实例）
func (m *Manager) GetAllDatabase() map[string]Database {
	m.mu.RLock()         // 加读锁，允许并发读取，不阻塞其他读操作
	defer m.mu.RUnlock() // 函数退出时自动释放锁

	// 创建一个新的map作为返回结果，克隆内部存储的数据库实例
	// 目的：避免外部直接修改管理器内部的database map，保证数据安全性
	result := make(map[string]Database)
	for k, v := range m.database {
		result[k] = v // 复制键值对到新map
	}

	return result // 返回克隆后的map
}

// CloseAllDatabase 关闭管理器中所有数据库实例的连接，并清空管理器
// 返回值：
//
//	error: 所有连接都成功关闭返回nil；若有失败，返回最后一个失败的错误信息
func (m *Manager) CloseAllDatabase() error {
	m.mu.Lock()         // 加写锁，防止关闭过程中被其他操作干扰
	defer m.mu.Unlock() // 函数退出时自动释放锁

	var lastError error // 记录最后一个发生的错误

	// 遍历所有数据库实例，逐个断开连接
	for name, db := range m.database {
		if err := db.Disconnect(); err != nil {
			lastError = err // 更新最后一个错误
			// 记录断开连接失败的日志，包含数据库名称和错误信息
			log.Errorf("Failed to disconnect database %s: %v", name, err)
		}
	}

	m.database = make(map[string]Database) // 清空数据库映射表，释放资源
	return lastError                       // 返回最后一个错误（若有）
}

// PingAllDatabase 检查管理器中所有数据库实例的连接是否有效
// 返回值：
//
//	map[string]error: 每个数据库的连接检查结果（key为数据库名称，value为错误信息：nil表示连接有效，非nil表示连接无效）
func (m *Manager) PingAllDatabase() map[string]error {
	m.mu.Lock()         // 加写锁（原代码使用Lock，此处保持一致；实际可考虑RLock，因Ping为读操作）
	defer m.mu.Unlock() // 函数退出时自动释放锁

	// 创建结果映射表，存储每个数据库的Ping结果
	results := make(map[string]error)

	// 遍历所有数据库实例，逐个执行Ping检查
	for name, db := range m.database {
		results[name] = db.Ping() // 存储检查结果（nil表示连接有效）
	}

	return results // 返回所有数据库的连接状态
}

/*
通过这个管理器，上层代码可以更简洁地操作多个数据库（如 “启动时添加 MySQL 和 Redis”“退出时关闭所有连接”），无需关心每个数据库的具体实现细节
*/
